<cfcomponent displayname="ScriptWriterFactory" output="no" hint="Factory bean for the ScriptWriter system.">
	
	
	<cfproperty name="JavaScriptCompressor" type="any" />
	<cfproperty name="CssCompressor" type="any" />
	<cfproperty name="REQUESTS" type="struct" />
	<cfset Variables.Instance = {} />
	
	
	<!--- init() --->
	<cffunction name="init" access="public" returntype="ScriptWriter.Factory">
		<cfargument name="JavaScriptCompressor" type="any" required="no" default="" />
		<cfargument name="CssCompressor" type="any" required="no" default="" />
		<cfscript>
			
			// Initailize the structure that holds individual request, region, and group data.
			initRequestStruct();
			
			// Compressors are optional.  Object checks are done later.
			setJavaScriptCompressor(arguments.JavaScriptCompressor);
			setCssCompressor(arguments.CssCompressor);
			setJavaScriptCompressor(arguments.JavaScriptCompressor);
			
		</cfscript>
		<cfreturn this>
	</cffunction>
	
	
	<!--- initRequestStruct() --->
	<cffunction name="initRequestStruct" access="private" returntype="any">
		<cfset variables.Instance["REQUESTS"] = StructNew() />
	</cffunction>
	
	
	<!--- getJavaScriptCompressor() --->
	<cffunction name="getJavaScriptCompressor" access="private" returntype="any" hint="Returns the JavaScript Compressor object.">
		<cfreturn variables.Instance["JavaScriptCompressor"] />
	</cffunction>
	
	
	<!--- setJavaScriptCompressor() --->
	<cffunction name="setJavaScriptCompressor" access="private" returntype="any" hint="Sets the JavaScript Compressor object.">
		<cfargument name="oCompressor" type="any" required="true" />
		<cfset variables.Instance["JavaScriptCompressor"] = arguments.oCompressor />
	</cffunction>
	
	
	<!--- getCssCompressor() --->
	<cffunction name="getCssCompressor" access="private" returntype="any" hint="Returns the CSS Compressor object.">
		<cfreturn variables.Instance["CssCompressor"] />
	</cffunction>
	
	
	<!--- setCssCompressor() --->
	<cffunction name="setCssCompressor" access="private" returntype="any" hint="Sets the CSS Compressor object.">
		<cfargument name="oCompressor" type="any" required="true" />
		<cfset variables.Instance["CssCompressor"] = arguments.oCompressor />
	</cffunction>
	
	
	<!--- getRequestStruct() --->
	<cffunction name="getRequestStruct" access="private" output="no" returntype="struct">
		<cfreturn variables.Instance["REQUESTS"] />
	</cffunction>
	
	
	<!--- openRequest() --->
	<cffunction name="openRequest" access="public" output="no" returntype="any" hint="Take a UUID as an argument. Uses this in tandem with createPerRequestTemplateStruct() to generate an empty structure in which to hold data for that request.">
		<cfargument name="sRequestID" type="string" required="yes" />
		<cfset StructInsert(getRequestStruct(),arguments.sRequestID,createPerRequestTemplateStruct()) />
	</cffunction>
	
	
	<!--- createPerRequestTemplateStruct() --->
	<cffunction name="createPerRequestTemplateStruct" access="private" output="no" returntype="struct" hint="Creates the basic structure used to hold per-request style and script data.">
		<cfset var local = {} />
		<cfset local.stRequestTemplate = StructNew() />
		<cfset local.stRequestTemplate["SCRIPT"] = StructNew() />
		<cfset local.stRequestTemplate["STYLE"] = StructNew() />
		<cfset local.stRequestTemplate["INLINE"] = StructNew() />
		<cfreturn local.stRequestTemplate />
	</cffunction>
	
	
	<!--- closeRequest() --->
	<cffunction name="closeRequest" access="public" output="no" returntype="any" hint="Removes the structure related to a request ID from ScriptWriter's memory.">
		<cfargument name="sRequestID" type="string" required="yes" />
		<cfset StructDelete(getRequestStruct(),arguments.sRequestID) />
	</cffunction>
	
	
	<!--- add() --->
	<cffunction name="add" access="public" output="no" returntype="any">
		<cfargument name="request" type="string" required="yes" />
		<cfargument name="type" type="string" required="yes" />
		<cfargument name="path" type="string" required="yes" />
		<cfargument name="region" type="string" required="yes" />
		<cfargument name="group" type="string" required="no" default="DEFAULT" />
		<cfargument name="minify" type="boolean" required="no" default="false" />
		<cfargument name="outputPath" type="string" required="no" default="" />
		
		<cfset var local = {} />
		<cfscript>
			
			// Create an object of the correct type
			local.oExternalElement = createElementObject(sElementType=arguments.type, sPath=arguments.path, blnMinify=arguments.minify, sOutputPath=arguments.outputPath);
			
			// Prepare the structs
			prepRegion(arguments.request, UCase(arguments.region), UCase(arguments.type));
			prepGroup(arguments.request, UCase(arguments.region), arguments.group, UCase(arguments.type));
			
			// Add the element
			addItem(arguments.request, UCase(arguments.region), UCase(arguments.group), local.oExternalElement, UCase(arguments.type));
			
		</cfscript>
	</cffunction>
	
	
	<!--- clear() --->
	<cffunction name="clear" access="public" output="no" returntype="any">
		<cfargument name="request" type="string" required="yes" />
		<cfargument name="type" type="string" required="yes" />
		<cfargument name="region" type="string" required="no" default="" />
		<cfargument name="group" type="string" required="no" default="" />
		
		<cfif ((arguments.group EQ "") AND (arguments.region EQ ""))>
			<cfset clearType(sRequestID=arguments.request, sType=arguments.type) />
		<cfelseif ((arguments.group EQ "") AND (arguments.region NEQ ""))>
			<cfset clearRegion(sRequestID=arguments.request, sType=arguments.type, sRegionName=arguments.region) />
		<cfelseif ((arguments.group NEQ "") AND (arguments.region NEQ ""))>
			<cfset clearGroup(sRequestID=arguments.request, sType=arguments.type, sRegionName=arguments.region, sGroupName=arguments.group) />
		</cfif>
		
	</cffunction>
	
	
	<!--- createElementObject() --->
	<cffunction name="createElementObject" access="private" output="no" returntype="any">
		<cfargument name="sElementType" type="string" required="yes" />
		<cfargument name="sPath" type="string" required="yes" />
		<cfargument name="blnMinify" type="boolean" required="no" default="false" />
		<cfargument name="sOutputPath" type="string" required="no" default="" />
		
		<cfset var local = {} />
		<cfscript>
			if (arguments.sElementType EQ "script") {
				local.sElementComponentType = "ScriptWriter.Script";
				local.oElementCompressor = getJavaScriptCompressor();
			}
			else if (arguments.sElementType EQ "style") {
				local.sElementComponentType = "ScriptWriter.Style";
				local.oElementCompressor = getCssCompressor();
			}
			return CreateObject("component","#sElementComponentType#").init(
				path=arguments.sPath,
				minify=arguments.blnMinify,
				outputPath=arguments.sOutputPath,
				compressor=local.oElementCompressor
			);			
		</cfscript>
	</cffunction>
	
	
	<!--- prepRegion() --->
	<cffunction name="prepRegion" access="private" output="no" returntype="any">
		<cfargument name="sRequestID" type="string" required="yes" />
		<cfargument name="sRegionName" type="string" required="yes" />
		<cfargument name="sType" type="string" required="yes" />
		<cfif (NOT getRegionExists(arguments.sRequestID,arguments.sRegionName,arguments.sType))>
			<cfset createRegion(arguments.sRequestID,arguments.sRegionName,arguments.sType) />
		</cfif>
	</cffunction>
	
	
	<!--- getRegionExists() --->
	<cffunction name="getRegionExists" access="private" output="no" returntype="boolean">
		<cfargument name="sRequestID" type="string" required="yes" />
		<cfargument name="sRegionName" type="string" required="yes" />
		<cfargument name="sType" type="string" required="yes" />
		<cfset var local = {} />
		<cfset local.stRequests = getRequestStruct() />
		<cfreturn StructKeyExists(local.stRequests["#arguments.sRequestID#"]["#UCase(arguments.sType)#"],arguments.sRegionName) />
	</cffunction>
	
	
	<!--- getRegion() --->
	<cffunction name="getRegion" access="private" output="no" returntype="any">
		<cfargument name="sRequestID" type="string" required="yes" />
		<cfargument name="sRegionName" type="string" required="yes" />
		<cfargument name="sType" type="string" required="yes" />
		<cfset var local = {} />
		<cfset local.stRequests = getRequestStruct() />
		<cfreturn local.stRequests["#arguments.sRequestID#"]["#UCase(arguments.sType)#"]["#UCase(arguments.sRegionName)#"] />
	</cffunction>
	
	
	<!--- createRegion() --->
	<cffunction name="createRegion" access="private" output="no" returntype="any">
		<cfargument name="sRequestID" type="string" required="yes" />
		<cfargument name="sRegionName" type="string" required="yes" />
		<cfargument name="sType" type="string" required="yes" />		
		<cfset var local = {} />
		<cfset local.stSubRegion = StructNew() />
		<cfset local.stSubRegion["DEFAULT"] = ArrayNew(1) />
		<cfset StructInsert(getRequestStruct()["#arguments.sRequestID#"]["#UCase(arguments.sType)#"],arguments.sRegionName,local.stSubRegion) />
	</cffunction>
	
	
	<!--- clearRegion() --->
	<cffunction name="clearRegion" access="private" output="no" returntype="any">
		<cfargument name="sRequestID" type="string" required="yes" />
		<cfargument name="sRegionName" type="string" required="yes" />
		<cfargument name="sType" type="string" required="yes" />
		<cfset StructClear(getRequestStruct()["#arguments.sRequestID#"]["#UCase(arguments.sType)#"]["#arguments.sRegionName#"]) />
	</cffunction>
	
	
	<!--- addItem() --->
	<cffunction name="addItem" access="private" output="no" returntype="any">
		<cfargument name="sRequestID" type="string" required="yes" />
		<cfargument name="sRegionName" type="string" required="yes" />
		<cfargument name="sGroupName" type="string" required="yes" />
		<cfargument name="oItem" type="any" required="yes" />		
		<cfargument name="sType" type="string" required="yes" />
		<cfset ArrayAppend(getRequestStruct()["#arguments.sRequestID#"]["#UCase(arguments.sType)#"]["#arguments.sRegionName#"]["#arguments.sGroupName#"],arguments.oItem) />
	</cffunction>
	
	
	<!--- prepGroup() --->
	<cffunction name="prepGroup" access="private" output="no" returntype="any">
		<cfargument name="sRequestID" type="string" required="yes" />
		<cfargument name="sRegionName" type="string" required="yes" />
		<cfargument name="sGroupName" type="string" required="yes" />
		<cfargument name="sType" type="string" required="yes" />
		
		<cfif (NOT getGroupExists(arguments.sRequestID,arguments.sRegionName,arguments.sGroupName, arguments.sType))>
			<cfset createGroup(arguments.sRequestID,arguments.sRegionName,arguments.sGroupName,arguments.sType) />
		</cfif>
	</cffunction>
	
	
	<!--- getGroupExists() --->
	<cffunction name="getGroupExists" access="private" output="no" returntype="boolean">
		<cfargument name="sRequestID" type="string" required="yes" />
		<cfargument name="sRegionName" type="string" required="yes" />
		<cfargument name="sGroupName" type="string" required="yes" />
		<cfargument name="sType" type="string" required="yes" />
		
		<cfset var local = {} />
		<cfset local.stRequests = getRequestStruct() />
		<cfreturn StructKeyExists(local.stRequests["#arguments.sRequestID#"]["#UCase(arguments.sType)#"]["#arguments.sRegionName#"],arguments.sGroupName) />
	</cffunction>
	
	
	<!--- createGroup() --->
	<cffunction name="createGroup" access="private" output="no" returntype="any">
		<cfargument name="sRequestID" type="string" required="yes" />
		<cfargument name="sRegionName" type="string" required="yes" />
		<cfargument name="sGroupName" type="string" required="yes" />
		<cfargument name="sType" type="string" required="yes" />	
		<cfset StructInsert(getRequestStruct()["#arguments.sRequestID#"]["#UCase(arguments.sType)#"]["#arguments.sRegionName#"],UCase(arguments.sGroupName),ArrayNew(1)) />
	</cffunction>
	
	
	<!--- clearGroup() --->
	<cffunction name="clearGroup" access="private" output="no" returntype="any">
		<cfargument name="sRequestID" type="string" required="yes" />
		<cfargument name="sRegionName" type="string" required="yes" />
		<cfargument name="sGroupName" type="string" required="yes" />
		<cfargument name="sType" type="string" required="yes" />	
		<cfset ArrayClear(getRequestStruct()["#arguments.sRequestID#"]["#UCase(arguments.sType)#"]["#arguments.sRegionName#"]["#arguments.sGroupName#"]) />
	</cffunction>
	
	
	<!--- combine() --->
	<cffunction name="combine" access="public" output="no" returntype="any">
		<cfargument name="request" type="string" required="yes" />
		<cfargument name="type" type="string" required="yes" />
		<cfargument name="outputPath" type="string" required="yes" />
		<cfargument name="region" type="string" required="yes" />
		<cfargument name="group" type="string" required="no" default="DEFAULT" />
		
		<cfset var local = {} />
		<cfset local.arFiles = getRequestStruct()["#arguments.request#"]["#UCase(arguments.type)#"]["#arguments.region#"]["#arguments.group#"] />
		
		<cfif (NOT FileExists(ExpandPath(arguments.outputPath)))>
			<cfset local.sCombinationSource = "" />
			<cfloop from="1" to="#ArrayLen(local.arFiles)#" index="i">
				<cfset local.sCombinationSource &= local.arFiles[i].getPreferredSource() />
			</cfloop>
			<cfset FileWrite(ExpandPath(arguments.outputPath),local.sCombinationSource) />
		</cfif>
		
		<cfset clearGroup(arguments.request,arguments.region,arguments.group,arguments.type) />
		<cfset local.oItem = createElementObject(arguments.type, arguments.outputPath) />
		<cfset addItem(arguments.request,arguments.region,arguments.group,local.oItem,arguments.type) />
	</cffunction>
	
	
	<!--- output() --->
	<cffunction name="output" access="public" output="no" returntype="any">
		<cfargument name="request" type="string" required="yes" />
		<cfargument name="type" type="string" required="yes" />
		<cfargument name="region" type="string" required="yes" />
		<cfargument name="mode" type="string" required="no" default="external" />
		
		<cfset var local = {} />
		<cftry>
			
			<cfset local.stRegion = getRegion(sRequestID=arguments.request, sType=arguments.type, sRegionName=arguments.region) />
			<cfset local.sRegionKeyList = StructKeyList(local.stRegion) />
			<cfset local.sOutputString = "" />
			
			<cfif (NOT StructIsEmpty(local.stRegion))>
				<!--- Loop through region groups --->
				<cfloop from="1" to="#StructCount(local.stRegion)#" index="i">
					<!--- Loop through group scripts --->
					<cfset local.arCurrentGroupScripts = local.stRegion["#ListGetAt(local.sRegionKeyList,i)#"] />
					<cfloop from="1" to="#ArrayLen(local.arCurrentGroupScripts)#" index="x">
						<cfset local.sOutputString &= local.arCurrentGroupScripts[x].getOutputFormat(arguments.mode) />
					</cfloop>
				</cfloop>
			</cfif>
			
			<cfcatch type="any">
				<!--- Either the region was empty or there was another boo-boo. Just output nothing --->
				<cfrethrow />
			</cfcatch>
			
		</cftry>
	
		<cfreturn local.sOutputString />
	</cffunction>
	
	
	
	<!--- dump() --->
	<cffunction name="dump" access="public" returntype="any" output="no">
		<cfargument name="blnAbort" type="boolean" required="no" default="true" />
		<cfscript>
			stDump = StructNew();
			stDump.Object = this;
			stDump.InstanceVariables = Instance;
		</cfscript>
		<cfdump var="#stDump#">
		<cfif (Arguments.blnAbort)>
			<cfabort>
		</cfif>
	</cffunction>
	

</cfcomponent>